﻿using Blm.Utils.Extensions;
using CodeMeter;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace VisionCloud.Utility.Security
{
    /// <summary>
    /// 加密狗（需要配置包文件）
    /// </summary>
    public class WibuDogHelper
    {
        private static Api cm;

        /// <summary>
        /// 获取已授权加密狗的序列号
        /// </summary>
        /// <returns>存在返回序列号，不存在返回空</returns>
        public string GetAuthDogSerialNumber(uint firmCode = 6001089, uint productCode = 13)
        {
            HCMSysEntry hcmse = null;	// handle of CmContainer or CmContainer entry
            cm = new Api();
            // Create a "subsystem Access" to the local subsystem
            CmAccess2 cmaccess = new CmAccess2();
            cmaccess.FirmCode = firmCode;
            cmaccess.ProductCode = productCode;
            hcmse = cm.CmAccess2(CmAccessOption.Local, cmaccess);
            if (hcmse == null)
            {
                return string.Empty;
            }
            var currentBoxInfo = cm.CmGetBoxes(hcmse, CmGetBoxesOption.AllPorts);
            if (currentBoxInfo.Length == 0)
            {
                return string.Empty;
            }
            else
            {
                return currentBoxInfo[0].SerialNumber.ToString();
            }
        }

        /// <summary>
        /// 读取加密狗保护区数据
        /// </summary>
        /// <param name="firmCode"></param>
        /// <param name="productCode"></param>
        /// <param name="boxIndex"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool ReadDogKey(out string data, uint firmCode = 6001089, uint productCode = 13, int boxIndex = 0)
        {
            StringBuilder sb = new StringBuilder();

            HCMSysEntry hcmse; // handle to Product Item to list

            if (boxIndex < 0)
            {
                data = $"boxIndex{boxIndex}超出范围";
                return false;
            }

            if (firmCode < 6000000)
            {
                data = $"公司码{firmCode}不正确";
                return false;
            }

            // Create an "Entry Access" to the CmContainer
            CmAccess2 cmacc2 = new CmAccess2();
            cmacc2.Ctrl = CmAccess.Option.Force | CmAccess.Option.NoUserLimit;
            cmacc2.FirmCode = firmCode;
            cmacc2.ProductCode = productCode;
            cmacc2.UsedRuntimeVersion = (uint)CodeMeter.Api.CmSetVersion(6, 0, 0);
            hcmse = cm.CmAccess2(CmAccessOption.Local, cmacc2);
            if (null == hcmse)
            {
                data = $"公司码{firmCode}产品码{productCode}找不到hcmse";
                return false;
            }

            var currentBoxInfo = cm.CmGetBoxes(hcmse, CmGetBoxesOption.AllPorts);
            if (boxIndex >= currentBoxInfo.Length)
            {
                data = $"boxIndex{boxIndex}超出范围";
                return false;
            }

            // Listing of entry consists of two parts.
            // First the fix parts:
            CmBoxEntry2 cmboxentry2 = (CmBoxEntry2)cm.CmGetInfo(hcmse, CmGetInfoOption.EntryInfo2);
            if (null == cmboxentry2)
            {
                cm.CmRelease(hcmse);
                data = $"公司码{firmCode}产品码{productCode}找不到存储单元";
                return false;
            }

            // Second the dynamic parts
            CmEntryData[] cmentrydata;
            cmentrydata = (CmEntryData[])cm.CmGetInfo(hcmse, CmGetInfoOption.EntryData);
            if (cmentrydata.Length == 0)
            {
                data = $"获取cmentrydata信息失败";
                return false;
            }

            for (int i = 0; i < cmentrydata.Length; i++)
            {
                string explanation = "";
                switch (CodeMeter.CmConvert.ToGlobalEntryOption(cmentrydata[i].Ctrl & (CodeMeter.CmConvert.Cast(GlobalEntryOption.PioMask))))
                {
                    case (GlobalEntryOption.ExtendedProtectedData):
                        if ((cmentrydata[i].Ctrl >> 16) == 250)
                        {
                            explanation = (System.Text.Encoding.ASCII.GetString(cmentrydata[i].Data)).Substring(0, (int)cmentrydata[i].DataLen);
                            sb.Append(explanation);
                        }
                        break;

                    case (GlobalEntryOption.ProtectedData):
                        sb.Append(ToHexString(cmentrydata[i].Data, cmentrydata[i].DataLen));
                        break;

                    case (GlobalEntryOption.SecretData):
                        sb.Append((System.Text.Encoding.Unicode.GetString(cmentrydata[i].Data)).Substring(0, (int)cmentrydata[i].DataLen));
                        break;
                }
            }
            cm.CmRelease(hcmse);

            //data = sb.ToString().Replace(" ","");
            var arr = sb.ToString().Split(' ', StringSplitOptions.RemoveEmptyEntries);
            StringBuilder stringBuilder = new StringBuilder();
            foreach (var el in arr)
            {
                char temp = (char)el.ToInt();
                stringBuilder.Append(temp);
            }
            data = stringBuilder.ToString().Trim();
            return true;
        }

        /// <summary>
        /// byte[]转为16进制字符串
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string ByteToHexStr(byte[] bytes)
        {
            string returnStr = "";
            if (bytes != null)
            {
                for (int i = 0; i < bytes.Length; i++)
                {
                    returnStr += bytes[i].ToString("X2");
                }
            }
            return returnStr;
        }

        /// <summary>
        /// 将16进制的字符串转为byte[]
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        public static byte[] StrToHexByte(string hexString)
        {
            hexString = hexString.Replace(" ", "");
            if ((hexString.Length % 2) != 0)
                hexString += " ";
            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < returnBytes.Length; i++)
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            return returnBytes;
        }

        private static string ToHexString(byte[] bytes, uint len)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            for (int i = 0; i < Math.Min(bytes.Length, len); i++)
            {
                sb.Append(string.Format("{0:X02} ", bytes[i]));
            }
            return sb.ToString();
        }

        /// <summary>
        /// 加密字符串（只支持大写字符串），首先要用wubi客户端，设置产品项，添加私密数据选项，分配最大为256字节的空间。
        /// </summary>
        /// <param name="firmCode">公司码</param>
        /// <param name="productCode">产品码</param>
        /// <param name="boxIndex"></param>
        /// <param name="keyStr">加密字符串</param>
        /// <returns></returns>
        public bool WriteDogKey(string keyStr, out string msg, uint firmCode = 6001089, uint productCode = 13, int boxIndex = 0)
        {
            StringBuilder sb = new StringBuilder();
            // create a sequence to encrypt
            byte[] source = System.Text.Encoding.UTF8.GetBytes(keyStr);

            if (source == null || source.Length == 0)
            {
                msg = "加密字符串有误";
                return false;
            }

            if (source != null && source.Length > 0)
            {
                foreach (var s in source)
                {
                    if (s < 65 || s > 90)//65-90
                    {
                        msg = "只支持大写字符串";
                        return false;
                    }
                    sb.Append(s);
                }
            }

            string cmd = $"cmboxpgm /qn1 /f{firmCode} /cau /p{productCode} /ppd0x{sb.ToString().Trim()} /cau";

            if (source.Length > 256)
            {
                msg = $"{keyStr}长度大于256字节";
                return false;
            }

            Process p = new Process();
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardError = true;
            p.StartInfo.RedirectStandardInput = true;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.Verb = "runas";//管理员方式
            p.Start();

            p.StandardInput.WriteLine($"{cmd}");
            p.StandardInput.WriteLine("exit");
            StreamReader reader = p.StandardOutput;
            string strLine = reader.ReadLine();
            List<int> list_pid = new List<int>();
            while (!reader.EndOfStream)
            {
                strLine = strLine.Trim();
                if (strLine.Length > 0)
                {
                    if (strLine.IndexOf("error") > -1)
                    {
                        msg = "写入失败";
                        return false;
                    }
                }
                strLine = reader.ReadLine();
            }
            p.WaitForExit();
            reader.Close();
            p.Close();
            msg = sb.ToString();
            return true;
        }

       
        
    }
}